﻿using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using AMatysiak.Methods;
using System.Collections.Generic;
using System;

namespace AMatysiak.Controls
{
    public partial class InvoiceGrid : UserControl
    {
        #region Decrets

        public class GridDecret
        {
            public string Name = "";
            public double Multiplier = 0;
            public string Unit = "";
            public string PKWiU = "";
            public double Netto = 0;
            public double Brutto = 0;
            public string Vat = "";

            public override int GetHashCode()
            {
                return Name.GetHashCode() + Multiplier.GetHashCode() +
                    Netto.GetHashCode() + Brutto.GetHashCode() + Vat.GetHashCode();
            }

            public override bool Equals(object obj)
            {
                if (obj is GridDecret)
                    return GetHashCode().Equals(obj.GetHashCode());
                return base.Equals(obj);
            }

            public override string ToString()
            {
                return "{Name} => {Netto}zł (VAT: {Vat})".FormatNamed(this);
            }
        }

        public GridDecret[] Decrets
        {
            get { return getDecrets(); }
            set { setDecrets(value); }
        }

        private GridDecret[] getDecrets()
        {
            List<GridDecret> decrets = new List<GridDecret>();
            foreach (DataGridViewRow row in grid.Rows)
            {
                if (row.Cells[0].Value == null) break;

                GridDecret d = new GridDecret()
                {
                    Name = getName(row),
                    Multiplier = getMultiplier(row),
                    Unit = getUnit(row),
                    PKWiU = getPKWiU(row),
                    Netto = getNetto(row),
                    Brutto = getBrutto(row),
                    Vat = getVat(row)
                };

                decrets.Add(d);
            }
            return decrets.ToArray();
        }

        private string getName(DataGridViewRow row)
        {
            return row.Cells[NameColumn].Value.ToString();
        }

        private double getMultiplier(DataGridViewRow row)
        {
            return row.Cells[MultiColumn].Value.ToString().ToDouble();
        }

        private string getUnit(DataGridViewRow row)
        {
            if (showUnit && row.Cells[UnitColumn].Value != null)
                return row.Cells[UnitColumn].Value.ToString();
            else
                return "";
        }

        private string getPKWiU(DataGridViewRow row)
        {
            if (showPKWiU && row.Cells[PKWiUColumn].Value != null)
                return row.Cells[PKWiUColumn].Value.ToString();
            else
                return "";
        }

        private double getNetto(DataGridViewRow row)
        {
            return row.Cells[NettoColumn].Value.ToString().ToDouble();
        }

        private double getBrutto(DataGridViewRow row)
        {
            return row.Cells[BruttoColumn].Value.ToString().ToDouble();
        }

        private string getVat(DataGridViewRow row)
        {
            return row.Cells[VatColumn].Value.ToString();
        }

        private void setDecrets(GridDecret[] decrets)
        {
            grid.Rows.Clear();

            foreach (GridDecret d in decrets)
            {
                int row = grid.Rows.Add(
                    null, d.Name, d.Multiplier, d.Unit, d.PKWiU, d.Netto, d.Brutto, d.Vat
                    );

                calculateNettoBruttoIfNeeded(d, row);
            }

            actualizeStatistics();
        }

        public bool DecretsCorrect
        {
            get { return validDecrets(); }
        }

        private bool validDecrets()
        {
            for (int i = 0; i < grid.Rows.Count - 1; i++)
            {
                if (!validRow(i))
                    return false;
            }

            return true;
        }

        private bool validRow(int rowIndex)
        {
            if (!validName(rowIndex))
                return false;

            if (!validMoney(rowIndex))
                return false;

            if (!validVat(rowIndex))
                return false;

            return true;
        }

        private bool validName(int rowIndex)
        {
            if (grid.Rows[rowIndex].Cells[NameColumn].Value.Equals(""))
            {
                grid.Rows[rowIndex].Cells[NameColumn].Selected = true;
                return false;
            }
            return true;
        }

        private bool validMoney(int rowIndex)
        {
            double tryParse;
            foreach (int col in new int[] { MultiColumn, NettoColumn, BruttoColumn })
            {
                string value = grid.Rows[rowIndex].Cells[col].Value.ToString();
                if (!double.TryParse(value, out tryParse))
                {
                    grid.Rows[rowIndex].Cells[col].Selected = true;
                    return false;
                }
            }
            return true;
        }

        private bool validVat(int rowIndex)
        {
            if (grid.Rows[rowIndex].Cells[VatColumn].Value.Equals(""))
            {
                grid.Rows[rowIndex].Cells[VatColumn].Selected = true;
                return false;
            }
            return true;
        }

        public void ClearVats()
        {
            foreach (DataGridViewRow row in grid.Rows)
                row.Cells[VatColumn].Value = "";
        }

        #endregion

        #region Fields and Settings

        public delegate void ChangedValueColumnsInfo(double sumNetto, double sumBrutto);

        public event ChangedValueColumnsInfo ChangedValueColumns;

        private const int NameColumn = 1;
        private const int MultiColumn = 2;
        private const int UnitColumn = 3;
        private const int PKWiUColumn = 4;
        private const int NettoColumn = 5;
        private const int BruttoColumn = 6;
        private const int VatColumn = 7;

        public InvoiceGrid()
        {
            InitializeComponent();

            Decrets = new GridDecret[] { };
            showUnit = true;
            showPKWiU = true;

            grid.RowsAdded += delegate { rowsChanges(); };
            grid.RowsRemoved += delegate { rowsChanges(); };
            grid.CellValueChanged += new DataGridViewCellEventHandler(cellValueChanged);
        }

        private void rowsChanges()
        {
            for (int i = 0; i < grid.Rows.Count - 1; i++)
                grid.Rows[i].Cells[0].Value = i + 1;
        }

        public new bool Enabled
        {
            get { return grid.Enabled; }
            set
            {
                grid.Enabled = value;
                grid.BackgroundColor = (grid.Enabled ? Color.White : SystemColors.Control);
            }
        }

        public new Color BackColor
        {
            get { return grid.BackColor; }
            set { grid.BackColor = value; }
        }

        private string[] vats;
        public string[] Vats
        {
            get { return vats; }
            set
            {
                vats = value;
                DataGridViewComboBoxColumn col = (DataGridViewComboBoxColumn)grid.Columns[VatColumn];
                col.DataSource = Vats;
            }
        }

        private bool showUnit;
        public bool ShowUnit
        {
            get { return showUnit; }
            set
            {
                showUnit = value;
                showValueChanged(UnitColumn, value);
            }
        }

        private bool showPKWiU;
        public bool ShowPKWiU
        {
            get { return showPKWiU; }
            set
            {
                showPKWiU = value;
                showValueChanged(PKWiUColumn, value);
            }
        }

        private void showValueChanged(int colIndex, bool visibility)
        {
            grid.Columns[colIndex].Visible = visibility;
        }

        #endregion

        #region Calculation money cells

        private bool interuptStop = false;
        private DataGridViewCell cell;
        private DataGridViewCell cellSec;
        private DataGridViewCell cellVat;

        private void cellValueChanged(object sender, DataGridViewCellEventArgs e)
        {
            if (grid.Rows.Count - 1 <= e.RowIndex) return;
            if (interuptStop) return;

            chooseCells(e);

            if (changedNettoOrBruttoCell())
            {
                calculateSecondCellValue();
            }
            else if (changedVatCell(e.ColumnIndex))
            {
                chooseMoneyCell(e);

                calculateOnVatChange();
            }

            actualizeStatistics();
        }

        private void chooseCells(DataGridViewCellEventArgs e)
        {
            cell = grid.Rows[e.RowIndex].Cells[e.ColumnIndex];
            cellSec = grid.Rows[e.RowIndex].Cells[
                e.ColumnIndex == NettoColumn ? BruttoColumn : NettoColumn];
            cellVat = grid.Rows[e.RowIndex].Cells[VatColumn];
        }

        private bool changedNettoOrBruttoCell()
        {
            return (new int[] { NettoColumn, BruttoColumn }).Contains<int>(cell.ColumnIndex);
        }

        private bool changedVatCell(int columnIndex)
        {
            return columnIndex == VatColumn;
        }

        private void chooseMoneyCell(DataGridViewCellEventArgs e)
        {
            cell = grid.Rows[e.RowIndex].Cells[NettoColumn];
            cellSec = grid.Rows[e.RowIndex].Cells[BruttoColumn];
        }

        private void calculateOnVatChange()
        {
            if (allCellsEmpty())
                return;

            if (nettoIsActive())
            { }
            else if (bruttoIsActive())
                swapCells();
            else if (nettoIsNotEmpty())
            { }
            else if (bruttoIsNotEmpty())
                swapCells();

            calculateSecondCellValue();
        }

        private bool allCellsEmpty()
        {
            return cell.Value == null && cellSec.Value == null;
        }

        private bool nettoIsActive()
        {
            return cell.Tag != null && (bool)cell.Tag == true;
        }

        private bool bruttoIsActive()
        {
            return cellSec.Tag != null && (bool)cellSec.Tag == true;
        }

        private bool nettoIsNotEmpty()
        {
            return cell.Value != null;
        }

        private bool bruttoIsNotEmpty()
        {
            return cellSec.Value != null;
        }

        private void swapCells()
        {
            DataGridViewCell cell3 = cell;
            cell = cellSec;
            cellSec = cell3;
        }

        private void calculateSecondCellValue()
        {
            interuptStop = true;

            if (cell.Value == null)
                zeroCell();
            else
            {
                calcSecondCell();
            }

            interuptStop = false;
        }

        private void zeroCell()
        {
            setTag(ref cell, false);
            cellSec.Value = 0;
        }

        private void setTag(ref DataGridViewCell cell, bool val)
        {
            cell.Tag = val;

            cell.Style.Font = actualizeFontStyle(ref cell, val);
        }

        private Font actualizeFontStyle(ref DataGridViewCell cell, bool val)
        {
            Font font;
            if (cell.Style.Font != null)
                font = cell.Style.Font;
            else
                font = cell.InheritedStyle.Font;

            FontStyle style;
            if (val)
                style = FontStyle.Regular;
            else
                style = FontStyle.Italic;

            return new Font(font, style);
        }

        private void calcSecondCell()
        {
            setTag(ref cell, true);
            setTag(ref cellSec, false);

            if (cellVat.Value == null)
                return;

            double cellValue;
            if (!double.TryParse(cell.Value.ToString(), out cellValue))
                return;

            setMainCellValue(cellValue);

            setSecondCellValue(cellValue);

            actualizeStatistics();
        }

        private void setMainCellValue(double cellValue)
        {
            cell.Value = cellValue.MoneyRound();
        }

        private void setSecondCellValue(double cellValue)
        {
            double vatRate = calcVatRate();

            double cellSecValue;
            if (mainCellIsNetto())
                cellSecValue = cellValue * (1 + vatRate);
            else
                cellSecValue = cellValue / (1 + vatRate);

            cellSec.Value = cellSecValue.MoneyRound();
        }

        private bool mainCellIsNetto()
        {
            return cell.ColumnIndex == NettoColumn;
        }

        private double calcVatRate()
        {
            double vatRate;
            string vat = cellVat.Value.ToString();
            if (vat == "zw." || vat == "")
                vatRate = 0;
            else
                vatRate = double.Parse(vat.Substr(-1)) / 100;
            return vatRate;
        }

        #endregion

        #region Actualization of stats

        public void RaiseActualizeStatistics()
        {
            actualizeStatistics();
        }

        private void actualizeStatistics()
        {
            try
            {
                calculateSum();
            }
            catch (Exception ex)
            {
                MessageBox.Show("Wystąpił błąd: " + ex.Message,
                    "Niepoprawne dane wśród pozycji faktury",
                    MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        double sumNetto = 0;
        double sumBrutto = 0;

        private void calculateSum()
        {
            sumRows();

            if (ChangedValueColumns != null)
                ChangedValueColumns(sumNetto, sumBrutto);
        }

        private void sumRows()
        {
            sumNetto = 0;
            sumBrutto = 0;

            foreach (DataGridViewRow row in grid.Rows)
            {
                if (isVatNotChoosen(row) || allMoneyCellsAreEmpty(row)) break;

                calcRow(row);
            }
        }

        private bool isVatNotChoosen(DataGridViewRow row)
        {
            return row.Cells[VatColumn].Value == null;
        }

        private bool allMoneyCellsAreEmpty(DataGridViewRow row)
        {
            return row.Cells[NettoColumn].Value == null
                && row.Cells[BruttoColumn].Value == null;
        }

        private void calcRow(DataGridViewRow row)
        {
            if (row.Cells[MultiColumn].Value == null || row.Cells[MultiColumn].Value.ToString().IsEmpty())
                row.Cells[MultiColumn].Value = 1;
            string multiValue = row.Cells[MultiColumn].Value.ToString();

            double multi = double.Parse(multiValue);

            sumNetto += multi * nettoValue(row);
            sumBrutto += multi * bruttoValue(row);
        }

        private double nettoValue(DataGridViewRow row)
        {
            return row.Cells[NettoColumn].Value.ToString().ToDouble();
        }

        private double bruttoValue(DataGridViewRow row)
        {
            return row.Cells[BruttoColumn].Value.ToString().ToDouble();
        }

        private void calculateNettoBruttoIfNeeded(GridDecret d, int row)
        {
            if (d.Multiplier == 0 || d.Vat == "")
                return;

            if (d.Netto != 0 && d.Brutto == 0)
            {
                DataGridViewCellEventArgs e = new DataGridViewCellEventArgs(NettoColumn, row);
                cellValueChanged(grid, e);
            }
            else if (d.Netto == 0 && d.Brutto != 0)
            {
                DataGridViewCellEventArgs e = new DataGridViewCellEventArgs(BruttoColumn, row);
                cellValueChanged(grid, e);
            }
        }

        #endregion
    }
}
